<?php 
/**
 * This file is part of DomSql.
 * 
 * DomSql is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomSql is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomSql.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */

/**
* @package DomSql
*/ 
class DomSql implements DomMonitorable {
	
	const DEFAULT_ALIAS = 'default';
	
	/**
	 * @var DomSql
	 */
	private static $i;
	/**
	 * Holds all activeconnections.
	 *
	 * @var Array
	 */
	private static $connections = array();
	/**
	 * Iterator which will be used to iterate result
	 * 
	 * @var String
	 */
	private static $iteratorClass = 'DomSqlIterator';
	
	
	
	
	/**
	 * Constructor is private, it uses Singelton pattern
	 */
	private function __construct() {}
	/**
	* Disconnects all connections
	*/
	public function __destruct() {
		self::disconnect();
	}
	/**
	 * @param String $className
	 */
	public function setIteratorClass( $className ) {
		if( is_a($className, DomSqlIterator) === true )
			self::$iteratorClass = $className;
		else
			throw new InvalidArgumentException( $className.' must be instanceof DomSqlIterator.');
	}
	/**
	* Initializes DomSql and connects to database. 
	* @param String $host
	* @param String $username
	* @param String $password
	* @param String $database
	* @param String $alias
	*/
	public static function init( $host, $username, $password, $database = NULL, $alias = DomSql::DEFAULT_ALIAS ) {
		
		if( self::$i === NULL ) {
			self::$i = new DomSql();
			if( DOM_MONITOR === true ) DomMonitor::gap('DomSql initialized', 'initialize');
		}
		
		self::connect( $host, $username, $password, $alias );
		if( $database !== NULL ) self::selectDatabase( $database, $alias );
		
		Dom::databaseConnected();
		
	}
	/**
	* Makes new connection to SQL server. Does not really connect, real connection will be made if first query is sent to database
	* If you want it to connect right away call realConnect after it.
	* @param String $host
	* @param String $username
	* @param String $password
	* @param String $database
	* @param String $alias
	*/
	public static function connect( $host, $username, $password, $alias = DomSql::DEFAULT_ALIAS) {
		self::$connections[$alias] = array( $host, $username, $password );
	}
	/**
	 * Connects to database using alias. Parameters must be set using connect method. Connect method sets up connection and this method actually connects.
	 * You should not call this method manualy, it will be called when first query is sent to database using same alias.
	 * @param unknown_type $alias
	 * @return unknown_type
	 */
	public static function realConnect( $alias ) {
		DOM_MONITOR && $t = DomMonitorEntry::c( __CLASS__, 'Connect to host <b>'.$host.'</b> with user <b>'.$username.'</b> (USING PASSWORD '.($password ? 'YES' : 'NO').'). Connection alias: <b>'.$alias.'</b>' );
		
		list($host,$username,$password,$database) = self::$connections[$alias];
		
		self::$connections[$alias] = mysql_connect($host, $username, $password, true);
		if( self::$connections[$alias] == false ) {
			throw new RuntimeException( 'Could not connect to '.$host.':'.$username.' (USING PASSWORD '.($password ? 'YES' : 'NO').') '.mysql_error(), mysql_errno() );
		}
		mysql_query('SET NAMES utf8', self::$connections[$alias] );
		
		if( $database != NULL ) {
			self::selectDatabase( $database, $alias );
		}
			
		DOM_MONITOR && $t->end();
	}
	/**
	* Select database
	* @param String $database
	* @param String $alias
	* @return Boolean
	*/
	public static function selectDatabase( $database, $alias = DomSql::DEFAULT_ALIAS ) {
		
		if( is_array(self::$connections[$alias]) === true ) {
			self::$connections[$alias][3] = $database;
		} else if( mysql_select_db($database, self::$connections[$alias] ) === false ) {
			throw new RuntimeException( 'Coult not select database: '.mysql_error(), mysql_errno() );
		}
		
		return true;
	}
	/**
	* if connection exists
	* @param String $alias
	* @return Boolean
	*/
	public static function connected( $alias = DomSql::DEFAULT_ALIAS ) {
		return (boolean)self::$connections[$alias];
	}
	/**
	* @param String $alias
	* @return Resource
	*/
	public static function getConnection( $alias = DomSql::DEFAULT_ALIAS ) {
		return self::$connections[$alias];
	}
	/**
	* Disconnects connection. If no arguments given then all connections will be disconnected
	* @param String $alias
	* @return Boolean
	*/
	public static function disconnect( $alias = NULL ) {
		if( $alias === NULL) {
			foreach( self::$connections as $alias => $link )  {
				if( !is_array($link) ) {
					mysql_close($link);
				}
			}
		} else if( self::$connections[$alias] && !is_array(self::$connections[$alias]) ){
			mysql_close(self::$connections[$alias]);
			unset(self::$connections[$alias]);
		}
		return true;
	}
	/**
	* Makes query to database.
	* @param String $query If left as NULL, then new DomSqlQuery object is returned.
	* @param String $alias
	* @return DomSqlQuery or resource if $query is empty
	*/
	public static function query( $query = NULL, $alias = DomSql::DEFAULT_ALIAS ) {
		
		if( $query === NULL ) return new DomSqlQuery();
		
		if( is_array(self::$connections[$alias]) === true ) {
			self::realConnect( $alias );
		}
		
		DOM_MONITOR && $t = DomMonitorEntry::c( __CLASS__, htmlentities($query) );

		if( self::$connections[$alias] == false ) {
			throw new RuntimeException( 'Connection with alias "'.$alias.'" is not connected!<br /> <strong>Tried to execute Query:</strong> '.htmlentities($query), 0 );
		}
		
		$resource = mysql_query($query, self::$connections[$alias] );

		/*if( microtime(true)-$time > 0.6 ) {
			$log = new DomFileLogger('sql');
			$log->log( microtime(true)-$time, $query );
		}*/
		
		DOM_MONITOR && $t->end();

		if( $resource === false && mysql_error(self::$connections[$alias])) {
			throw new RuntimeException( mysql_error(self::$connections[$alias]).'<br />Database alias:'.$alias.'<br /><strong>Query:</strong> '.htmlentities($query), mysql_errno(self::$connections[$alias]) );
		}
		
		return $resource;
	}
	/**
	* @return Int last insert id
	*/
	public static function lastInsertId( $alias = DomSql::DEFAULT_ALIAS ){
		return mysql_insert_id( self::$connections[$alias] );
	}
	/**
	 * Returns number of affected rows by last update or insert query.
	 * @param String $alias
	 */
	public static function affectedRows( $alias = DomSql::DEFAULT_ALIAS ) {
		return (int)mysql_affected_rows(self::$connections[$alias]);
	}
	/**
	* Make select query to database. Query is not sent to database before DomSqlIterator is rewinded!
	* @return DomSqlIterator
	*/
	public static function selectQuery( $query, $alias = DomSql::DEFAULT_ALIAS ) {
		return new self::$iteratorClass( $query, $alias );
	}
	/**
	 * <code>
	 * // sample 1
	 * foreach( DomSql::keyListQuery('SELECT id,name FROM users','id','name') as $id => $name );
	 * 
	 * // sample 2
	 * foreach( DomSql::keyListQuery('SELECT id,name,age FROM users','id',) as $id => $row ) {
	 * 	echo $id.' - '.$row['name'].'('.$row['age].')';
	 * }
	 * </code>
	 *
	 * @param String $query Mysql query
	 * @param String $key This value will be used as array key
	 * @param String $value This value will be used as value. If not added then whole row is set as value.
	 * @param String $alias
	 * @return DomSqlKeyListIterator
	 */
	public static function keyListQuery( $query, $key = 'id', $value = NULL, $alias = DomSql::DEFAULT_ALIAS) {
		return DomSqlIterator::c( $query, $alias )->setIndexColumnName($key)->setValueColumnName($value);
	}
	/**
	* Make select query to database that returns only one row
	* <code>
	* // sample 1. Return one row
	* $row = DomSql::selectOneQuery('SELECT id,username FROM users WHERE id=1 LIMIT 1');
	* 
	* // sample 2. Return only one value
	* $total = DomSql::selectOneQuery('SELECT count(*) as n FROM users', 'n');
	* echo 'There are '.$total.' users.
	* </code>
	* @return Array
	*/
	public static function selectOneQuery( $query, $field = NULL, $alias = DomSql::DEFAULT_ALIAS ) {
		
		// using this if because DomSqlIterator doesn't use parseCurrent method so we dont need to use iterator to return this result. (its faster not to use)
		if( self::$iteratorClass === 'DomSqlIterator') {
			$r = self::query($query, $alias);
			
			if( $r ) {
				if( $field === NULL ) {
					return mysql_fetch_assoc( $r );
				} else {
					$r = mysql_fetch_assoc( $r );
					return $r[$field];
				}
			} else
				return $field === NULL ? array() : NULL;
				
		} else {
			$data = new self::$iteratorClass( $query, false, $alias );
			$data = $data->current();
			if( is_array($data) === false )
				$data = array();
			
			if( $field === NULL ) {
				return $data;
			} else {
				return $data[$field];
			}
		}
	}

}
?>